/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

#include "crypto/s2n_mldsa.h"

#include "crypto/s2n_libcrypto.h"
#include "s2n_test.h"
#include "testlib/s2n_testlib.h"

#define S2N_MLDSA_FILE_PREFIX      "../pems/mldsa/ML-DSA-"
#define S2N_MLDSA_PUB_SUFFIX       ".crt"
#define S2N_MLDSA_PRIV_SEED_SUFFIX "-seed.priv"
#define S2N_MLDSA_EXPANDED_SUFFIX  "-expanded.priv"
#define S2N_MLDSA_BOTH_SUFFIX      "-both.priv"

int main(int argc, char **argv)
{
    BEGIN_TEST();

    /* The AWSLC API version was bumped to 32 when ML-DSA support was added.
     * See https://github.com/aws/aws-lc/commit/404fe0f8a79ca0f28118990a06e921b63035991c
     */
    if (s2n_libcrypto_awslc_api_version() >= 32) {
        EXPECT_TRUE(s2n_mldsa_is_supported());
    }

    if (!s2n_mldsa_is_supported()) {
        END_TEST();
    }

    const char *suffixes[] = {
        S2N_MLDSA_PRIV_SEED_SUFFIX,
        S2N_MLDSA_EXPANDED_SUFFIX,
        /* AWS-LC does not currently support the "both" key format,
         * so s2n-tls does not currently support the "both" key format.
         * S2N_MLDSA_BOTH_SUFFIX,
         */
    };

    /* The expected signatures were generated via the `openssl dgst -sign` command
     * as confirmation that our implementation matches other known implementations.
     * For example:
     * echo -n "hello pq signature world" | openssl dgst -sign ML-DSA-44-seed.priv -hex
     */
    const struct {
        const char *key_size;
        const char *expected_signature;
    } test_cases[] = {
        { .key_size = "44", .expected_signature = "ea3686f8889adb644af3d2fd0ec07baea038596a7f3ce6b28723fdee582db0f4d00938327aca2480a903c19171e8c34e901210c626348e6d8564bc78a5ee50c784503598fcede72ba707a5657d5779a57a2f7fd1b7d51e43de9ebd61a0aedd693729ff03a9406771a8cb6ab48c4a5d6eeb25d8a47680c187135b12800910de6a09e94767ccb31f2abff26ed81568d1a150b9c1ff042c0b27e32031cdc87bde9b2b819fb099d1826efdd4fb71c07b6ace929bb1f33d179d33ef158e11dbaf1ffd962b62ca717fa2ba1f228b2df19a6671e47d5d1b673538535aa546314a91641de645590a0c94c9f7a4400331c291b72ae54c272531a39966d67f0d6657c820926ca00bcf4dbe6909c740a4ca5e48735b7a5c898b8aa938b89939bc38ff398781ccb4c5eed4667d0be70e30a7d4e8fecf32d1db22ec76aa05c0b5fca1c7fed5ce50ab13046bc14314f06438576235536f12b6c1faf16487b18d378b264edc150cdd03d2fb736c5482adaa918f11d91d08d023393bad9a1ad0cb36fef02d0d6c8f9a67550879e339bb945d0792d1ccc01d35b26f7c15aded5184a346c10acb61114422fb2813621f5ffb6c4cfb08f34b52159349f0b8eee67203c90a486da4071ee92b0068488cdccbd3cbfa06a0318ab7ac447b4426653ccee04a2f3e12e9dfc1da5801ef8159b81715e62a63d1053d0b2368e4f70fdcb7869e11fc97cb5221fb5d1d072c527d608d9f24e7d77527db1a79558bbe21b31b79eea27b2dee8d76764e16e20ebdfed50992ad7c25cae842b327c5199c334ba9ea07f09e368ed0f89a33f6468aaaac1f4175b149e64b7eeaf52ecce4eb263b2a5868e875e6a0dd60ed32395917c43779300cb01ac826c3474d9dc3a389a40a23be1e4befe96f17921c5607834b256b96f60fc684159d7215494700bb0932791347e2a4de6e631b2344a61470f0c78f59f869614742677ee3b0e3375f4363ddc0b71590db23b938c1a4ccf093035c1575fb08ac8337c13509c801015d66336a9d2ec81c012cb233b1fdd10fde2c9ee39185b9bec20ceeaccc8d853d32574a8591696b7c3ccd032f8b4576d25a7d57d1cb6d07b3d49cbe4356201f248bb9f3476f7ea79ba306635de3b324b13f35e6bdc4865f3b2c8946983c9ce69b0f908522c332108cb17df73710aa5e8a4ba5ce1671fbd9c9565e4cc298ae2efaabbe43e782af191cddb4ff105317693bab78c6334e253b3383a107bf9fd293807c3f6cec4dca6c584e3fe180d640a18868897300ce6e68f10b81da43a663127c100372cadd9cb8e9044d77b78260f72680a6d40396690bc211eb744d2951934f2de7b08b0fa2a5a7a209feb118907c28fe2b363fc86e2f3f22be9e2c8b05376d2a414480a194ec04bda6f10bb860cd0dfde27b42a72f08656924e05d75a0b0a68793c808915add0f1e30a9d5cfc3e3b8838d6fa6ffe103fb6b7e0df2bed9d9dd42e41711fee81fa7fcf30a183fd45dce8f51ad9c477a8b65f81db92370043fad4943b9009118d0e93ce63ec021fe71c675b8b3953eb93dab18dabe8a36752f6d6b9006980ad93f7daea764a85efc9c242919d330e04b2fd55b1563cf0c4ca4c1a7b31ba4632f47fd6d553e812cb31bdf7236fc69c48036d15e454c9cb84076e4f1125a492e4c3e1dc1e961ec2fad9942cab195fae6e5c62aacb8f522727555001de36ae0d4d247f1db9e3bb888a03d8eef90bbddf826cb27ba285c8c10e1058278e3225f6220afbc818f7ab6a3b0a4583627d9d79d15c059cc58a650f7772b1298e400eeb5eb75c5d24c82b55cbf971ebb6620ad93dfe6b1d46294e3b39fc941775341a5acf10ceae7268961d5f435ea07e99444913597c4a4385a184b7cb0c4d3e83eb2658c18796779d6cebb94dc31b03f9584a97aa39d4ce2913fdfb85f4cf3562aa214c8b93473d4eb287f075201497f819f19e4f94ee6a255003361d397e7da810be998380ecd7d7acf451f6a50120ae5e74301695f2e0fc222620417c6e75c9a03d811f959f5cce5013a9111a196a0620bef94920112cbc862ed2c82fe286159306e05f848a92e6aa24d057230bd5744fd1b5ecb41652a7a1445d87717412d5979480841841a6abc88df10698b3cf7c18830fd0450b6e53cd0a8be9e969a4ddbb47daedf083ea919eb8c800cdb561a53359c1c0b2791c6c163d6016973a628a83eb50914830cb95865e82b3e682c3ad9632b0e44a73159335ff6a5e2f22c4ff1d27afdfcc083b6a7886683a2bbaae5b731d80ef7af2a6cad2a831d9c3f20f086b8330ed107ddb29bc02197fba7c61f6481127ae8cea493290d6afd351e349c00250eba7386dc2ec09319f1d3cbc370f12d28f882cfd388f6f225b14abc6e5132e836f168632d8790cae19ff67eb390ab80d4f444f4bd0ef3d4062625440d4091bc9ede134a709a3c6d800ef8bae4393b05aeffd5fbb34bc5233214ecf5207332714d1447b651c76c3330018540c0f64b1edc9712c9255c3e14c89f98d2f0ff38962f34ecce1e75727be6932a3bef750632ae271254446e7f46d7c05b62f29bbf141334bf89935742b06c956a86aff903c275b938c92012bc893dad61246be585c61f800d767d44118ed2a0688f81bd301cf08e2f6d5b18ab28700734ac169c4341073ed3a3eab9f2871a7c18ff41b831ff8b1a566917d6423660deff9f7fb4644cd8fac2e35bd8eefedcabc6a5196a1b9945aaed29be8fa0a7d9174ba2c296fdc12c493f3305d413ac2359a523a2da13b41528d6b73ce56f30588cba677c75223a95a85c1d83301d869a6ea0e2ebceba643114d1723fc78e4a6d27ff88d458a66482671abd588ac26412db0a6e7bc503b7706fc0981c53be833251ebe496ad938b1dea33b15bc792018afa48d6f34297ec9383af844da1f68945533b4fac4693febf5e682f3e539e4eeb9965826e3ad2037c579be830446d253dd1cb853ef0bceeadcbd824c142d413636301e3140e562837f50a8f364c84de07375cf88ce3a45d49e18d40fca26a4d721439ea40c9ee122c0b0eee4ba649df22addb1cc1fb710888d0bc8dbf5e9022ad2384263648676123ebd7b12a9f089c024faadfeb3a7ce8f52051d280e889b7b8002bf50678b667d93e7a7d4d54f9418ed8d2a22ac505df43e258087bf45c622d750ad45a505e72dda9112c87af9cbbd72958a2bf392284f87612011c6b349425153d18a72fa9f74e44f495694135ee7791555ef21dcfd20e51c8b1b9d4ab1b84e3ccb75bd4f1f92127c1858beb5b4110392900347b656f922929106a570b2ce8b72b2d35474c52649da61432394d59666a71798e939db8bfc7d1d8e1042537616f738e91acbebfc8dbe7ef061718313b3e4f676f73778d9ea8c5d1f3fbfc00000000000000000000000000000000000000091b2a3d" },
        { .key_size = "65", .expected_signature = "d564fd8982dffdbdf1187acfadf688a90babc0305ebc0b895aee10ddbe61538b713b53a141f73499be4a18e2ab55e26cf0d0fc2c90ce6bc4db634e64e290c94b8a1efc67404c9ca94ba84a7f85a281983ee0840ef093b1d8aa4eb4e3faa2ad3b63a0857e01099cd109a64119e4f858e9e08fc0c592c30ceeaf766002f8aac835f6737016aec12c71e317dce1b7a5d65c997b11b93190163fdcd86ddc3eb6cfbf51a16edd38e941aa248e7a577a2b716448d710c3a03a8ef34bc24044f9991533a2178588e9fd423984372cae727ad4b9c28b59a9c4862e1d87b5f04637db466ecf12fcac39a02011b28660f4e14639ccb2859cf46c0ffe732867094c69d60dec19ef527563af47ab478b9682111130ac3cbe5b4ee08fd04956d5edd486112897fbddedc22cadb66426e6a3fa6d351c56b9ec6404cbf82f7b1e6e99fce85714dc395051942e84923f9dcdd0b91c1ece3f6a4abb105e1300e2ccb0a45ae14d4fa68233d6b14d869597331a3dffebfdb9675ef0943bd69a6fbe880c4ae31fa432a58cfc85e04090ea5c8b5b7ad30a08a008194599603dd110bea7f45a2c624c92dad4184624dde68f82575179675e66c96e9e15745185de56e49003878fd26dc2912c5fd53b005b6edda461af0a7dc45e35ed6cea041a423d8cb5e27bd95ee49da58db869b15cab4148807caaeff0916fbfa3f6fba9f81a35665d11c625aea4168822bfe37b025e3dadab8c8a43ec29f012f1722e011a6ef724146646b18c7df0a1bf3b973df2ad47d2e4eb0bc70f351e02a5d68a81f13f8c08d65ba78c33cffa3c0a0fa9b224bd12aafebb233caf984acf5f129afe5985dc6765347f98378e352ac662d7b91ac65d1a172d3c0cf83fb9b120de6c9183829b59e0bf66fe1290f51334e0a69774365d68d504e47b23cca196a93f88d6922c31bdba9631d2a62b1229eaa7444bbae73df6150abcc7c706c272b5d323b97ba643c4840c9e3d3668a826e4a66f83169150437c42a37fff755c81b9ab4d9c831ba06941768e14777ed091e9bd274476f30ff520415a5d8d26cd343ae9085cedac3aec7bc6412fa88086d1c1e803535d796eb0455af93ca2a46894d75bf6c30720bdd2903f43c051b96bcf6c3cd83ae189655b7eb03c4169eb43e5d04e890da44c876274a2a2703974fdf19bd85ea58f870492e48cb3817cf426b171524fdd80096a88a73a0dba0d1201d6d27ce9e6e7d9422fdca3a76fada4a7af8b264464b8e5628cda1b43c6a6fd7f6a0b3bd474b5a7f1a4f55604d570367e00b33d9a4fc82ddf28c9275b63a63be2bb02c268363c586b49c66c4e1efd6cfc3b2e9135841996fe7a87f5e5dceef083336379a85968711375db7f8dabce916b3ec3502f071a98ce62e766a3879f91aa53fb4ec449a36b4f383c16f6587c4eaee55b257ac8033a6d934fd5992feddded1d62db755e1bbc3baee7030b3cc06fe4d7d60de8dbc7077a984310a0c77dbe9e032fba116f3f929514142efd4e23d82f9cefa7daec7c62b001140df8bab939da83d91aff577827415540f0001fcfa57462d20d17453bc91ea7fde7b6acbed8cb31023abc68728763d0fddb28009fcb0b7ff0269cf7dda6da7d22c51d45f284cd7867b3373b64fe9b200df755d4b60eff43d733a9dbb11efc595e23faef0f61baf5a72e90b70d3a657fa0e4fe1a88d6c071efaaf688ed68a5893a114108c329cac6ea65b2a03703500643715202796c707e6015b44d26e062f2698ab6b12c8f7cb054d0a58face39a403ab78bef0c4d64e7d4fd5241337ee46d2523b03b8e541d61e70a7f0398cdba6c739eceaf4035a7f9690257e9fabea9420833498be61b8f94610e184fb4c8f6f0bc93ddebd8430d09fcf6ccef5e5db4629c4d55acd9113473024d5450d5742a904234a7302d4923d4750662d68e2c9a7e2e27196f8dde994cf3836e5df769ad0ec7974aa04d56214180b6538ac78a1982e3f282171ccaf15f55ca5d336ab844cbd9c745b02f4a1b86ead2542caa0e58dc50fe480b5ed032b53501315591141b61d56074cd7fe5a64f78fe49fa4d3177d4f680c27a8a5c6f497f09c2dd4793f5b35d0c682d02aa191f81b9a0ebaf75914ac3128605427657d7bf549f5e9b32ca2c3a4bee362cbbd60535667ce626b60b920aa5e91955bd255533d9ba455e2738351cced65e8a459eed4e940227089542ca00329c3a611dd193388a3810d7027f5916e80be500fb6980f411647d391ab988266f28933bcdc1bc6e0b10f315b5b515c5506ebc086737dfe1064d98657f8c0277417dd49b60ef052386bc921f07d8d9f743bd6886a9d25319d2b0ba249d08cf8f756e3a25166eb4c86edb26991d8130b2b03e7447b10f05f798578d72f7598681789b27d57f1f9363b7fe0c3679684db59fe1be90c5437121b15bc278ff4335c02d2b160377e406e6229e8fe9c3456919a108d4934c767d49a79e0659383eda23a3b923fc40aa3e2ba82ffb6953baacdac2c346f0339528b428f2e8f18294d6c6e831789c3afe069a6eca36d431fcaacac0df7d2eb1d5598eb66621e2c88073c46cb643ce0b4dd7691e6fa64ff300956bb2e5985bf50d0f457a7abf42ff52aac9f43d2d4b86006f9f3f41cc067a2ca9fe0f64efe9d28425b2facdfd5dedc559277ce8389ffe43c42db2a0f3c78e3f828364e3ae39ca00ed2e6f78a40613d6360ccd664e4454a157086a2c1370fa4d69c4cb268e2e034905a5043edd139ee5840db204c2129053b4c14a19252f240343c79872c431d11f05ae6155ec3f4d004cbe3d42cf17c131b67b1f436b55d9fa2ad3f3ecb47173c0330de1cbde5f7d0361463f8e4f7529f663d2fe0e644f7f77dc5ac8e4292c9bd892765ebc25d412f03fd5f1920249124059a86924a2cc33cf513712e3a37e82e2368ea8d583fc9ac2bf9c80d1c0cc1be22db7e25691e83beff3442c5497f34a14c76daf911d6ece7003eeb7aa37675dd1b24f45bfff2395e7b82ca4049d2534754ad6041486f23d7dbcbe0872687d6e0b4f389239fa816c2c485a494885b733c587df1fa928b1be344ed7e0d45213b6c4f890758b4a2af80e2014c6edf51312d0c7c37842727d740b10790b30cf73aac6e05833224ea2263a0e004e61f462e085bdd6680440d2b3cc73460f03a89847b462991516a8694d8f7aa937108ead809ea643d94f7b8866859368eb09c456ddfc6d3802a7722bd552160253d914c3f4cbf6a9c9d98515fe2540d3838588715e310285ebec0d1400bf48ff2ab1fdaf3dbc26a05cef2f424cef50c56a39f81ca05f175762833e609f63b37fd03582c61847b4d9006626b94f7fb5d9db78f76d6fde6663486cfbd8e1fd5f3902a224a2bbe0ed3a7447ba03b4659d8e7d3acf7f20e200c6422a77e1e72b13396f8bc70acb326edda5429a1c7b1af10e217d3567add105b79ee599a0c53158b967a1a63f240152f0bec9ef17cca622375b6316577ecf21abc81937733cbea48ac39ffe74fc7e958787485835664f42cbbcc41ead9c635ca8bef935d8d853019c837fafd48e539ca57c345342c218319ea40e0fd74054364edf85458513e6cff7cf7ee0206f10ba9c0db09e87e499c616b3972f6de7008427102a88ed37c5ed045489d770add44e7bb59dc199225e40356cec6987ef17bbc7db7e181062b2d6736b90c2bd459f8750024aa9e2580be7703e7725f7d087bfe20bcfa8933a221958e02c97335f93469198403fed0984ad4ccbed573bc6338fc5e4fb817e14a033f7003ce042c120238a4988415977625d0ebe1819b1166b9451d756a72a2d62564ff761ff907d4fb321e0308cd5665dfce6f528f41c8dc7777c70c64062eaa564a6bcd3c33a123c23df5e594f5d759b8078c16d10ec676f75564794a9848f56b08555fec956dbbb2f6bc9960314199bff870a5944135e6e9e11ea286df14f2b350217d5c667027867b32df3e79d5e3242ac0fe929cf015783d865c547d1adbe1d79a2738aa9f469ae9940571103d8211a6ba75eee10c960922cbc5198f652e1495b624069bda9791bd37634c35d4ed056103d4e03999464d68d1a7f7a25e400f7e4126d1b7eeb9976992b2725efd23415c1968010ef81975f2903119b77ff41a5a0be6f0120213e51833d6c7af8dde6ac35a03fb11f742cf094ae68c9384c464e86b1fc74a96bfdb5ea6cc4184d6588acec66b5ea03fb39ea594383933a057360eca404a70ac3f507d9e518bee81fe5b3fb7dad45496231fd753e2b3a4584ea113aba6d2818c6424e162c568ac2c2b0267c23e354ab0312d945428a53608cb66eca45c31fd305264c32bb2bf7375520974811b3b5dda17c5371f4738e390bbde92e1905234de0250d5420e6241fae37ecafc714e8c3e1d99a43b1aace098f092b9813ffe3dbbd1f04753ab5dc9ee6588e548bc08856f6c4ac0bdada39bd83197569ccddff611f7b8c5927b7f8a6329250d179f76377390ce1ae15afe7cbb2b4bf60de86257f27692548add5dd90d49b26eee22030a2c35ed5f66b574210441ed7287485ac2a8f112dd69554a01ab773a71efb0ece2298717fb024f66fb0935ccda54ee9f1b8a1c83300230b235b809cb6dff8072b9ba6b3d3d5e7e964eb121a5781bae8f94d547383888a97dedff0f700000000000000000000000000000000020a13151c27" },
        { .key_size = "87", .expected_signature = "d7a756245209fc1002f2f5113a8c5be5c4edccd4e60ec8a33663aa12a2e09024105e62bfcd8d5a3ee50418c5adc83de9bc2d227190508a4a4399e68f1a5d6b82152ff456375bb892203db784248c504ca8b99b964d593790d7ca8e1e9fc9e94a64659c02db5bf4ea0968410f238db1db3c6b65324dd8018da20002b355738ed8eb7ebf043be38c81958a3c5f064beda8bf9d18af2ae2e214ec79d3718c759a76bbc94f3e189013d7365bb1f413ba2583105aab1b6ffc645c21c6f9f495f5091e9f1d83e1f5421b1cd2380ba67e33b8f06decace5675cc765114dbb5e7459889d8160d0019e4572a9034f6487067cf157309b17a5ef99fde15717a1c78935a9d36d80ce2d6085e2832d5782ae65c5887d75be5bdc8e3899a72ff7adbbf78ed887ee4cba2d5957074721f35cd1170a448ef9485037d250d36cb4af589d4a2a1b1d6556fb76fe9c7d293a585e886880c22166d65c569ebb66329c78983b17de04edf37bb6f083750d721151856ea2b07a8e89ef102c2a6947f76670a69986b45b6ad91417d4c2639060ef6df356c5faf56f8aeb847d919a15d1a160bb50101dd333ddc60cfe0f5094131ea165590d9951d396a6997a8b97108d4cda68f9c568481377634b1093d9a1979460b9fa409a5879443ad26027213ba6e7022a5bdfa1c6eb9d0791fa96c9590d4be44c4e18c34608b9b80ccc9a5e395fe903ff07ee1a572f6c5535a04c8c0fee4c2a8bdeb81781b131da8b79e411d01b75ad5ff45a4dcdf88b7507ee4f15daaa49e2bd5bd32313b7aac045c3fb52abd9a756e5e24ad0b1cf08f472121dfc3f630147999963a3d715a2b62047391aa3dc47b02a4080790c335f4c3f8ec81f1d5e75fd638a3ee57c41eab1db9bcf2c7788090736ba69838bcf30efd22c1dc799130dcd0b6b3fdbcd3e185dbef6ff284c244b49b0575364eacfb4b368d2593628c4d1fb58b5b26e65f3d13055ec102eceb25d31095a844d7c54fa9889a550f8c55eb50f590737e831b24e0c54f32a86d8f3c296d0b8232382a47254b0d58204d76c8e7accb3011347427c761d1deefd8f427468f1809892a10abb760e6810dd8fd67cfa093f9c31a96d9ece782cb70b9bd4526891a22b5197892ba0549f83e2661163a4d77d8af35530a9bc498f8fb564e476d1f9a3ab230492f7c5dce62de879c6173d27a0dbf30dadea4ab8104c9b8f9b1c933b7b151c6b31d86004a34fcb77c7af4d7914eaa9ea8700e0dbbaf88b2e531bb3e8d09f4a578b7a1e1485e53b5b99a6fdfe6b348e2095cced1b70305c5520753b516409e2b0a621591b67049c995214df4c4b058f954f61f852cbfd0dbe8adac46e15c4f798e68015e11d110d99e5675492174db3e384d7785705e3046a5350746a0c36b18eb6a27cb82f2b408323835a90900d34bc8b9d477294b96d2507796d235a13146f61c0ef64f0abb2df992cc025683b5bead50bfbd33414c8a074aec3fd5afc73bf8f4f954fe8a52b524d872f10fd35ed9cac3ebbb3fa7c04fda6f56390ecfffd46712cef0af2d5dcd31750deb1fd67ada949622242d77c15c1066b89a35e2ccc23502bd6f4f3cf5f33f23cacb4b2015478d1b1fb2b2c40591fdc75d5a903a919563d803614d175f4bbb27245f5a44493e04a6a716398b2ed263ddc416ff1998db1076fd306b804221520804139b5733e885dacae6c7404c6eefc7868a0836a2a65395de323afe8a877cc55dd1016fabfcd17caa8926a6739271294fa62765611f25160e55b2055ae35c214d91a5ba44e31e9e65bae83382b1f56b05204c10d25fc2b157d80c1aa93b92f7591422bff4ad04bbae7cb2d19db941159245a145352a4f3c268f69ca778651ea6d9fbdee814d6bb25a32126d9877ce304c977205a3659c51126f1878b3739f7304b427a3b4bed9682e14825a35755a5537769e34ae916f0aba6c89ea3d57299dea9e2711ee1d7e09be779f04b22babd41c05fc6c862c498f1ff37ac9777c88617089d6b53bd0fc1fcbb34a3042b5468e0a7ffa9be73ac30e6d1ddb953c381c132e32be706af7b134d02616c3e27006aec1fe2f9261d5d913ad72144ce23d56b99dcdc1880460c82e7fd2d2fe68a75214f9cf609a5ea1d28722731097dd8fbf5a9fb2732dc2974595b5999f14e8d6603837df53d21c4999cdf4e4e0c1703212dd391f97f217f9aa7858b38b22778aa5790af3de8188d6bae662d9f4141b655f47ea28d4a813b15b3f67211b3c4dbd22c1605fca487c3de07425413c260605f883c82bd5f6bb106153f75b3c77fb8843666529d8056d14c90cd7f0f228769feda87d1bb65fdf0b888d7f7b10e0b3aaf11d70217bd2b26d12b48e4a50b33b71ed7df709327f77e04ce9076261076b59e103233c78b2a7a753e17708f67ffdd12150b478518f60202ff1ad0ccb62a92bec0de4250f8f3b2411587bb70cf42c8b3a04ef280cb9161c71ea7bdf3cfc00ae48c6250ea61274c7e4b84acfba91c962bbcd8c9a58a0599a5e6421670df839393d2f5e32c3d0c1779067d332f5850fe46169e61302ac966e6d58845f05760b5c9ca0636f5e892a33ec0da68d2a566dbf7e24642bc79c1ea08532392e11fc9c4fca4278bd15665fc69e23053f33ccb756e71cb02967fce9b278abdf8f57f4c89b318eda1303fab484624ea970dfe27f20ffea634d5e418444b741c707a33a98774a368661daaa09eab43603325b431f0d03fcaac3718c00461aa1e214a49c5b3bac0be6520b69acfc97e62a600860a4ec7de611a67a8d622131ad38b6d3343daad14d03c1c0448bd09fcc71c0a1aeec0e977e5593cffb055de291e8592c927b3633c907ba9d493d505c0ebfb56bb8ef552f5133f970168e5e72e8352aa0d27daf0c15c2812b5fa3ece151b6a0a2a1e8565f71ea90237e787fae6f8212384a33b7765d642d53aad1715d53ae950c9054ff5f68ce1ad692c83686af0aea70397d7fe6ff668c53b2aa5bbc4544508c2dca922a8591572c64f4266af34eb4d1042a95680da5be74126b88616fe8d359602352a9fe029f3d9cb78296f2fe1331090acb2eda716e1d985e9606ce493855f3ef39ee1a51bd5fb38d065e074f12a076cbab4c97ff3e6ad36635fa38a80f6d84ae605cdcd5dd5bb5c87e77c33dd3357d7f7812dd034ae28c30478de418a6204f001d9ca8c11f501b91800bbc434ada434a7a76c020297e890487ef34dc30063d33ff8a86cc265a73d29c2d5d68da1fd8a077eacce5462ac37d4041b612a3f441b0713aa34f9471115980fbaaef4bf2bbda5708aa7df1b28cf1515b7326d7edf3c6ba397a7ff5c96fa87b07e400a8e575cef794b914096475425ea9d773503ddb2fd8f8b037c6c467c3c7c29e384bb59a45310a2e31bfdb0002c44cc1c94e13bc0c787769782d100f08792606aa4c63f11db17d584346145176fa9c9eca8655023704e7c9be2938265204fde9917337e254bf15c206baa99e121b8275e489b1ab759c9ff937acd9ef2bcf0509a69086275cd04a00f5178dc58038943b172bef29273371e44954f07baa88f61318901bb05e391343109a6f03ed47766f0b3e02f083230ce705a6e4dcf3b3ca7af493ca8dc0023d63c7c201e7cbd866c98fa3a3390219b11b6615377942bd6014cfa8807ff832bcd0355fd6ed37a33b1bb51c39692dd020dc90d55dea5f3997346c7edd35ae8a15381ac0b156ca43691c1c523bf2832e5ffba39e5f4ae791f93155757d51e0bf43bae91a00549368a7a72163de12c30f0071ad2f496233af55458b3c8edb802b35a13391e6fca2335a8216b678639372bfacd4cf114fea1230bead47ea9069f501bcc7f042f7e6f0bd162c5b6c17bd95ccb5a15b9c058870a94f1b55699eeabd5bbf5651281c6d2056049819f6f85a610f1ba88d15f3170f54545d0bf734dc7bde6a58374e837df51135cb37b4b51af98d6b71d82a20153d125bd23446f3db5ce8e4945a5b0bc216bc330744ff0e330c2d631705947271a99bfefb2dce7e01a5ec60f3be039d92856528899d0ba7a5dcb2f6e058b7edcdba4cdc8bc129d2f66f5a3a504590164a26d4a0d0568dc0ba7e31fdb47bc354d9608e76da55117d067e0ddfb00f2696f13edba838154979bffc92e05c9ca8290e06eae4475776f903cb55a7341f5d9fd7c0f45f72fc12603a610b18d704e39597ca1115f390f7d9632b4dae4f3451dde58b6c08a0f6e64472d3a6af8e71b03df11d5f4576d1252a743294794db415dad4684cde2f66b8bf4c49e957a9d5131b46228a47ab7d76cfc61af330787dec14918c6a55e2b1e11f925f6b46a968c1070424c218678dadd693a362227d6a7c9eaea7f2835d8f2393ed0ad8a40f6636742d2a5078793cc280d4a76d929725b18f1fa12bec5820b91f565330213c4e6bfc2bd3e07207f856d83e04d58f0856ae6c7ea78b617362d614f04b693f8236d70bf5195b5a0a58cb17c5b98935cb08b9c8de65be945a6650554e9e5ca65d520f6c478b347a9056585c1ab2eb90696eaed390186dc4db5701572799fe80dd25d8538ecc74d22e2d583df4a43189640982fef8c13430a0827632d22fc6e3e69227a2cd4545b3fa46c611a91b36c61677c58dbea16f9de30e8ac149823cc848434c4b9b3ce5ce1cb868d57439f692e12c8f8a185ea89af21bceaeb950843a6f618a4517e7b622394107c5f0709147f8fea43cd31b9f5324fc42b3deae7e33b120d2d473b0a324885c58aff81f7de8e1bbc5aad5b29f5b9c978b41f385cb9dddea63c5d9ffb92b52b4baaf7887eddf94ee56d21e63dde55269655fb6595d18c450d412132d91434c2fb7d48405f1923e6e97c346ec00041d4bcc8c72a21b55e5775076a7a5b982ef3af3361fa8babf21d84fdba725952daaa1e00a4317b259c015a591d5895b828a48787cc94f271fcf9530240b98334fd30b50eb0584f07029654daac2aa7bae73ed33154b7e3acdd992a695035ae1a41dd2a3d6dc0e9a72901bf85265be493168e2bb1dc21be4d477790ca28164f75c50e5d2ac784e03e70c95a6479016f981cbbcd2737fea4db76671b9568182723370934dd0c834f9215667b8a16e06b955ddc0b322344619876391a53be1da2d5d9082417821b31dcb334472b19894b3542e158fd03944bcf13293b40a8387a8309d921a2e23804928aacafdc31a0f9b4d747dffc21d879032a086d78c4513c138000ec11fa59fc2deb1bc96263faf4f52a18e4c3254f230830bf1df3ca43194f41b4185f2f3c3c4d5662dfb130b3996f1e6cae5c7f29533d02fbd225191e3c66c12687329c67c51a3f624370c044bc8dcfc646dfeb85cb3b149e3e10bcdb592520dcd7ae3270a1b300f3804038192521e83dba74d6fcd053f4ee39c612545207fe132df3cf50f24cee96a7ffdddbc1706239a80ccf5d71a73e8be5415a0f5fb417d469f2f622c21d9a8452ba6b4c6fece94bd756aa329ee788a750a8ec613296a06909c12d38bec971dab2020dc8e4e85c795fb4f6f42a8085da80b4fd33ebeba05db13fdcf65066fc84634b08e2f22a892083a3d34ba329db4358822f4b8cb5a33af2f829758ca94ca93ee3d1ad1b25c6f7acf55089521516fca7910c06195d3bb43120ec5738b85d1b94129b55b2e485e7b414fd86d1f6e799123e5dd5659c4795bc3597908d651c322a60b106c1f0f9d892dedfa140072c4bc0e1a56539bb4a3ebed92cce0878c9c9df99a96a8c51bc32226b40e87171388f2e75bf4ff8659499753f8a81da3671cea51d3cb4026cfd48d982e31856f7ef1dfbefdc70f109c73b8064b2d98014b1735157bca60f7318a53511aef8f0b56ec4192cfc295a7c99b73e395a38084377fb141e5f2eb25b5080284f7518333946e9a81160a83fdcebc4331b26f36b29f1e690c2750906dfe62576f10f41559bc3afab6eb4c80ed4e0af4c8a3a8a6650995852a855e90d32f59bed9e6c586e479d1c3540e4fcdb372e982e7abc17cb40b93766b37a1a92798a9f205575e10c9959117b5b0b1e3571cbd4eff993c8e1b4656a062e57cf19539a671c8e857b59cfae3ba4ee45b1e64a82de2ffdb9757f5479e694df33cd7819a9fb7a2676cd5f5859b67d92188b22ef88b1f18fec039317b87b9f83fdd652cf0419a797573a0d86eaf768036a169ad8df4fbb152d7893f965bdae3b59d40b4aaf8a0194e1ad34bf35dd3cb01bfc4599819aa22cc97fdc59b9d998a67b498f2c33d3e317439297fb199e49301a334c33a65ad83e7867f5a7e85e5854747037e988b1b18de3b2a0e7cbfadfef1c729b0f56f2c6c44f016ee3acc12cf597bb74ce6904a4c615b27ebd7e5ed64fddc7144abbb17ff6b9b21f96c2d795b2e616d7e0a23ccb2dc8665404de9b2b90ff52f17b5a64a224c3da8f7eba082aff113cbba7f82b9f608883ef9fc2b1cfd18b0b3b0b103a4144677fcfd3e9f9357e8193acc0dbdf416f717fabbae24b506b8894f901142b41dd314170727ca5cceb4d4e5ba9adc5f5f9363741455479b8dc0000000000000000000000000000000a12191f242c343c" },
    };
    const char test_message[] = "hello pq signature world";

    for (size_t i = 0; i < s2n_array_len(test_cases); i++) {
        const char *key_size = test_cases[i].key_size;

        /* Test every combination of private key formats */
        for (size_t suffix_i = 0; suffix_i < s2n_array_len(suffixes); suffix_i++) {
            const char *suffix = suffixes[suffix_i];

            char pub_key_path[100] = { 0 };
            snprintf(pub_key_path, sizeof(pub_key_path), "%s%s%s",
                    S2N_MLDSA_FILE_PREFIX, key_size, S2N_MLDSA_PUB_SUFFIX);

            char priv_key_path[100] = { 0 };
            snprintf(priv_key_path, sizeof(priv_key_path), "%s%s%s",
                    S2N_MLDSA_FILE_PREFIX, key_size, suffix);

            DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free);
            EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain,
                    pub_key_path, priv_key_path));
            EXPECT_NOT_NULL(chain);

            /* Test: private key is ML-DSA */
            struct s2n_pkey *private_key = chain->private_key;
            {
                EXPECT_NOT_NULL(private_key);
                EVP_PKEY *pkey = private_key->pkey;
                EXPECT_NOT_NULL(pkey);

                s2n_pkey_type pkey_type = 0;
                EXPECT_OK(s2n_pkey_get_type(pkey, &pkey_type));
                EXPECT_EQUAL(pkey_type, S2N_PKEY_TYPE_MLDSA);
            };

            /* Test: parsed public key is ML-DSA */
            DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free);
            {
                EXPECT_NOT_NULL(chain->cert_chain);
                EXPECT_NOT_NULL(chain->cert_chain->head);

                s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN;
                EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type,
                        &chain->cert_chain->head->raw));
                EXPECT_EQUAL(pkey_type, chain->cert_chain->head->pkey_type);
            };

            DEFER_CLEANUP(struct s2n_hash_state master_input = { 0 }, s2n_hash_free);
            EXPECT_SUCCESS(s2n_hash_new(&master_input));
            EXPECT_SUCCESS(s2n_hash_init(&master_input, S2N_HASH_SHAKE256_64));
            EXPECT_OK(s2n_pkey_init_hash(&public_key, S2N_SIGNATURE_MLDSA, &master_input));
            EXPECT_SUCCESS(s2n_hash_update(&master_input, test_message, strlen(test_message)));

            /* Test: can successfully sign test data */
            DEFER_CLEANUP(struct s2n_blob signature = { 0 }, s2n_free);
            {
                DEFER_CLEANUP(struct s2n_hash_state input = { 0 }, s2n_hash_free);
                EXPECT_SUCCESS(s2n_hash_new(&input));
                EXPECT_SUCCESS(s2n_hash_copy(&input, &master_input));

                uint32_t size = 0;
                EXPECT_OK(s2n_pkey_size(private_key, &size));
                EXPECT_SUCCESS(s2n_alloc(&signature, size));

                EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_MLDSA,
                        &input, &signature));
            };

            /* Test: can successfully verify actual signature */
            {
                DEFER_CLEANUP(struct s2n_hash_state input = { 0 }, s2n_hash_free);
                EXPECT_SUCCESS(s2n_hash_new(&input));
                EXPECT_SUCCESS(s2n_hash_copy(&input, &master_input));

                EXPECT_SUCCESS(s2n_pkey_verify(&public_key, S2N_SIGNATURE_MLDSA,
                        &input, &signature));
            };

            /* Test: can successfully verify expected signature */
            {
                DEFER_CLEANUP(struct s2n_hash_state input = { 0 }, s2n_hash_free);
                EXPECT_SUCCESS(s2n_hash_new(&input));
                EXPECT_SUCCESS(s2n_hash_copy(&input, &master_input));

                S2N_BLOB_FROM_HEX(expected_signature, test_cases[i].expected_signature);
                EXPECT_SUCCESS(s2n_pkey_verify(&public_key, S2N_SIGNATURE_MLDSA,
                        &input, &expected_signature));
            };

            /* Test: can successfully reject invalid signature */
            {
                S2N_BLOB_FROM_HEX(invalid_signature, test_cases[i].expected_signature);
                invalid_signature.data[0] = invalid_signature.data[0] + 1;

                DEFER_CLEANUP(struct s2n_hash_state input = { 0 }, s2n_hash_free);
                EXPECT_SUCCESS(s2n_hash_new(&input));
                EXPECT_SUCCESS(s2n_hash_copy(&input, &master_input));

                EXPECT_FAILURE_WITH_ERRNO(s2n_pkey_verify(&public_key, S2N_SIGNATURE_MLDSA,
                                                  &input, &invalid_signature),
                        S2N_ERR_VERIFY_SIGNATURE);
            };
        }
    }

    END_TEST();
}
